package cn.itcast.hotel.service.impl;

import cn.itcast.hotel.dto.RequestParams;
import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.service.IHotelService;
import cn.itcast.hotel.vo.PageResult;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;

@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {

    @Autowired
    private RestHighLevelClient client;

    @Override
    public PageResult search(RequestParams requestParams) throws IOException {
        //创建搜索请求
        SearchRequest searchRequest = new SearchRequest("hotel");
        //构建DSL的参数
        buildDSL(requestParams, searchRequest);
        //发送请求
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        //解析结果
        //最外层的hits
        PageResult pageResult = getPageResult(response);
        return pageResult;
    }

    /**
     * 封装响应结果
     * @param response
     * @return
     */
    private  PageResult getPageResult(SearchResponse response) {
        SearchHits sh = response.getHits();
        long total = response.getHits().getTotalHits().value;
        System.out.println("total = 总条数" + total);
        SearchHit[] hits = sh.getHits();
        List<HotelDoc> hotelDocList = new ArrayList<>();
        for (SearchHit hit : hits) {
            //取出_source这部分的hotDoc
            String json = hit.getSourceAsString();
            //json转hotDoc对象
            HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
            //获取排序字段sort [] 给前端返回距离
            Object[] sortValues = hit.getSortValues();
            if (sortValues != null && sortValues.length > 0) {
                System.out.println("sortValues = " + sortValues);
                Object sortValue = sortValues[0];
                if (sortValue != null)
                    hotelDoc.setDistance(sortValue);
            }
            //高亮处理
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if(!CollectionUtils.isEmpty(highlightFields)){
                HighlightField highlightField = highlightFields.get("name");
                if(highlightField != null){
                    String newName = highlightField.getFragments()[0].string();
                    hotelDoc.setName(newName);//将高亮标签中的结果 设置到返回给页面的对象中
                }
            }
            hotelDocList.add(hotelDoc);
            System.out.println(hotelDoc);
        }

        //封装PageResult的对象
        PageResult pageResult = new PageResult(total, hotelDocList);
        return pageResult;
    }

    /**
     * 构建DSL参数
     */
    private static void buildDSL(RequestParams requestParams, SearchRequest searchRequest) {
        String key =  requestParams.getKey();
        Integer size = requestParams.getSize();
        Integer page = requestParams.getPage();
        String sortBy = requestParams.getSortBy();
        String brand = requestParams.getBrand();
        String city = requestParams.getCity();
        String starName = requestParams.getStarName();
        Integer minPrice = requestParams.getMinPrice();
        Integer maxPrice = requestParams.getMaxPrice();
        String location = requestParams.getLocation();

        //构建DSL参数
        //第二个需求 添加brand ,city, starName price的过滤
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        if (StringUtils.isEmpty(key)) {
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        } else {
            boolQueryBuilder.must(QueryBuilders.matchQuery("all", key));
        }
        if(!StringUtils.isEmpty(city)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("city",city));
        }
        if(!StringUtils.isEmpty(brand)) {
            boolQueryBuilder.should(QueryBuilders.termQuery("brand",brand));
        }
        if(!StringUtils.isEmpty(starName)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("starName",starName));
        }
        if(!StringUtils.isEmpty(minPrice)) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(minPrice));
        }
        if(!StringUtils.isEmpty(maxPrice)) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(maxPrice));
        }
//        //对价格排序，距离排序
//        searchRequest.source().sort("price", SortOrder.ASC);
        if(!StringUtils.isEmpty(location)) {
            searchRequest.source().sort(SortBuilders.geoDistanceSort("location",new GeoPoint(location)).order(SortOrder.ASC)
                    .unit(DistanceUnit.KILOMETERS));
        }
        //酒店竞价
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(
                boolQueryBuilder,
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                //过滤条件
                                QueryBuilders.termQuery("isAD",true),
                                ScoreFunctionBuilders.weightFactorFunction(10)
                        )
                }
        ) ;    //.boostMode(CombineFunction.SUM);

        //评价降序 价格升序
        if(sortBy.equals("score")){
            searchRequest.source().sort("score",SortOrder.DESC);
        }else if(sortBy.equals("price")){
            searchRequest.source().sort("price",SortOrder.ASC);
        }
        //高亮设置
        searchRequest.source().highlighter(
                new HighlightBuilder().field("name")
                        .requireFieldMatch(false)
        );
        //分页
        searchRequest.source().from((page - 1) * size).size(size);
        //复合查询
        searchRequest.source().query(functionScoreQueryBuilder);
    }
}
